home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / UNHQX.ZIP / unhqx.pas < prev    next >
Pascal/Delphi Source File  |  1995-03-07  |  10KB  |  392 lines

  1. (* UNHQX.PAS, Turbo Pascal 7.0 object-unit to decode Mac BinHex files     *)
  2. (* ---------------------------------------------------------------------- *)
  3. (* by Robert Rothenburg Walking-Owl, <robert.rothenburg@asb.com>          *)
  4. (* -- CopyLeft 1994 - Feel free to use and modify as needed, but please   *)
  5. (*                    only distribute unmodified source code. If you make *)
  6. (*                    any improvements, please let me know.               *)
  7.  
  8.  
  9. unit UnHQX;
  10.  
  11. (* Inline code has been used for improved speed and smaller size, though  *)
  12. (* the 'original' Pascal code is included in comments to facilitate port- *)                   
  13. (* ing to other systems or flavors of Pascal.                             *)
  14.  
  15.  
  16. interface
  17.  
  18.     (* Buffer size is somewhat arbitrary.  Larger buffer sizes should  *)
  19.     (* decode faster. A better method would be to check available mem- *)
  20.     (* ory and allocate an appropriate sized-buffer...                 *)
  21.  
  22. type
  23.      TBuff = array [1..4096] of byte;
  24.      pBuff = ^TBuff;
  25.  
  26.   HQX = object
  27.          private
  28.           fif: ^file;
  29.           LastChar: Char;
  30.           RLE: Byte;
  31.           DBuffSz,
  32.           DBuffPtr:  Word;
  33.           DiskBuffer,
  34.           Bit_Buffer: pBuff;
  35.           procedure UpDateCRC(c: Word); virtual;
  36.           procedure PutBits(b: Word); virtual;
  37.           function ReadChar: Char; virtual;
  38.           procedure Fetch; virtual;
  39.           function Retrieve: Char; virtual;
  40.           function Decode(C: Char): Byte; virtual;
  41.          public
  42.           CRC,
  43.           Origin,
  44.           FilePtr: LongInt;
  45.           Cur,
  46.           Ptr: Word;
  47.           Loc: Byte;
  48.           Header: record
  49.                     FName: string[63];
  50.                     Version:  Byte;
  51.                     FType,
  52.                     Author: array[1..4] of char;
  53.                     FileCRC,
  54.                     CRC,
  55.                     Flags:  Word;
  56.                     DataLen,
  57.                     RsrcLen: LongInt;
  58.                   end;
  59.           constructor Init(var f: file; Orig: LongInt);
  60.           function fCRC: Word;
  61.           function fGetC: Char; virtual;
  62.           procedure fGetBlock(var Block; Size: word); virtual;
  63.           function fGetW: Word; virtual;
  64.           function fGetL: LongInt; virtual;
  65.           procedure fSeek(Position: LongInt); virtual;
  66.           procedure fSkip(Position: LongInt); virtual;
  67.          { procedure fRewind(Position: LongInt); virtual; }
  68.           destructor Done;
  69.         end;
  70.  
  71. implementation
  72.  
  73. const
  74.  (* Bit_Sizes[x] = 1 ShL (x-1) *)
  75.  {  Bit_Sizes: array [1..8] of byte = (  1, 2, 4, 8, 16, 32, 64, 128); }
  76.  
  77.   NUL = #00;
  78.   TAB = #09;
  79.   LF  = #10;
  80.   FF  = #12;
  81.   CR  = #13;
  82.   SP  = #32;
  83.  
  84.   RLEMARKER = #144; (* 0x90 = RLE marker *)
  85.  
  86.   cTBuffSz = SizeOf(TBuff);
  87.  
  88.  
  89. function SwapLong(x: LongInt): LongInt; assembler;
  90. asm
  91.     MOV AX, [BP+6]
  92.     MOV DX, [BP+8]
  93.     XCHG AX, DX
  94.     XCHG AL, AH
  95.     XCHG DL, DH
  96. end;
  97.  
  98. procedure HQX.UpDateCRC(c: Word);
  99. var
  100.   i: Byte;
  101.   Temp: word;
  102. begin
  103.   Temp := CRC;
  104.   asm
  105.                 MOV CX, $0808
  106.   @BitLoop:     SHL c, 1
  107.                 TEST Temp, $8000
  108.                 JZ @SkipConst
  109.                 SHL Temp, 1
  110.                 AND Temp, $FFFF
  111.                 XOR Temp, $1021
  112.                 JMP @SkipShift
  113.   @SkipConst:   SHL Temp, 1
  114.   @SkipShift:   MOV AX, c
  115.                 SHR AX, CL
  116.                 XOR Temp, AX
  117.                 AND c, $00FF
  118.                 DEC CH
  119.                 OR CH, CH
  120.                 JNZ @BitLoop
  121.   end;
  122.  (* --- Pascal code to do the same as the above inline code --- *)
  123.  { for i:= 0 to 7 do begin
  124.       c := c ShL 1;
  125.       if (Temp and $8000)<>0
  126.         then Temp := ((Temp ShL 1) and $FFFF) xor  $1021
  127.         else Temp := Temp ShL 1;
  128.       Temp := Temp xor (c ShR 8);
  129.       c := c and $FF;
  130.     end; }
  131.   CRC := Temp;
  132. end;
  133.  
  134. function HQX.fCRC: Word;
  135. begin
  136.   UpDateCRC(0);
  137.   UpDateCRC(0);
  138.   fCRC := CRC;
  139. end;
  140.  
  141. procedure HQX.PutBits (b: Word);
  142. var
  143.     Num: Byte;
  144.     PPtr: Word;
  145.     Hold: pointer;
  146. begin
  147.   Hold := Bit_Buffer;
  148.   Num  := Loc;
  149.   PPtr := Ptr;
  150.   asm
  151.                PUSH DS
  152.                LDS SI, Hold
  153.                MOV BX, PPtr
  154.                MOV AL, Num
  155.                MOV CX, $20           { num := 6 (Bit_Sizes[6] = 32;) }
  156.     @BitCycle: CMP AL, 0             { is Loc=0?                     }
  157.                JNE @NormLoc
  158.                MOV AL, $80           { Loc := $80                    }
  159.                INC BX                { inc (Ptr);                    }
  160.                CMP BX, cTBuffSz      { is Ptr > SizeOf(TBuff)?       }
  161.                JNA @PtrOk
  162.                MOV BX, 1
  163.     @PtrOk:    MOV Byte Ptr DS:[SI+BX-1], 0
  164.     @NormLoc:  TEST CX, b
  165.                JZ  @Continue
  166.                OR  Byte Ptr DS:[SI+BX-1], AL
  167.     @Continue: SHR AL, 1
  168.                SHR CL, 1
  169.                CMP CL, 0
  170.                JA @BitCycle
  171.                MOV PPtr, BX
  172.                MOV Num, AL
  173.                POP DS
  174.   end;
  175.   Ptr := PPtr;
  176.   Loc := Num;
  177.  
  178.  (* --- Pascal code to do the same as the above inline code --- *)
  179.   {
  180.   num := 6;
  181.   repeat
  182.    if Loc = 0
  183.     then begin
  184.       Loc := $80;
  185.       inc (Ptr);
  186.       if Ptr>SizeOf(TBuff) then Ptr := 1;
  187.       Bit_Buffer^[Ptr] := 0;
  188.      end;
  189.    if ( (b and Bit_Sizes [num] ) <> 0)
  190.      then Bit_Buffer^ [Ptr] := Bit_Buffer^ [Ptr] or Loc;
  191.    Loc := Loc ShR 1;
  192.    dec (num)
  193.   until num = 0;
  194.   }
  195. end;
  196.  
  197. function HQX.Decode(C: char): Byte;
  198. const
  199.   Table: string[64] =
  200.    '!"#$%&''()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr';
  201. var d: Byte;
  202. begin
  203.  
  204.  (* --- Pascal code to do the same as the above inline code --- *)
  205.  { d := Pos(C,Table);
  206.   if d=0
  207.     then Decode := $FF
  208.     else Decode := Pred(d); }
  209.   asm
  210.                 MOV SI, Offset Table+1
  211.                 XOR BX, BX
  212.                 MOV AL, C
  213.   @LookUpLoop:  CMP AL, [SI+BX]
  214.                 JE  @FoundMatch
  215.                 INC BX
  216.                 CMP BL, 64
  217.                 JL  @LookUpLoop
  218.                 MOV BL, $FF
  219.   @FoundMatch:  MOV @Result, BL
  220.   end;
  221. end;
  222.  
  223. function HQX.ReadChar: Char;
  224. begin
  225.   if DBuffPtr > DBuffSz
  226.     then begin
  227.       BlockRead(fif^,DiskBuffer^,SizeOf(TBuff),DBuffSz);
  228.       DBuffPtr := 1
  229.      end;
  230.   ReadChar := Chr(DiskBuffer^[DBuffPtr]);
  231.   inc(DBuffPtr);
  232. end;
  233.  
  234. procedure HQX.Fetch;
  235. var C: char;
  236.     i: Word;
  237.     j: Byte;
  238. begin
  239.    i := 4; (* 4 encoded chars <-> 3 raw chars *)
  240.            (* No. chars fethced related to buffer size... *)
  241.    repeat
  242.      C := ReadChar;
  243.      if (C<>CR) and (C<>LF) and (C<>TAB) and (C<>FF) and (C<>SP)
  244.       then if C = ':'
  245.        then begin
  246.          PutBits(0);
  247.          i := 1 (* Set an EoF flag needed! *)
  248.         end
  249.        else begin
  250.          j := Decode(C);
  251.          PutBits(j)
  252.         end;
  253.      dec(i);
  254.    until (i=0) or (DBuffSz=0);
  255. end;
  256.  
  257. function HQX.Retrieve: Char;
  258. begin
  259.   Retrieve := Chr(Bit_Buffer^[Cur]);
  260.   inc(Cur);
  261.   if Cur>SizeOf(TBuff)
  262.     then Cur := 1;
  263. end;
  264.  
  265. function HQX.fGetC: Char;
  266. var C,R: Char;
  267. begin
  268.   if RLE<>0
  269.     then begin
  270.         R := LastChar;
  271.         dec(RLE);
  272.       end
  273.     else begin
  274.       if (Cur+1)>=Ptr { Cur+3 }
  275.         then Fetch;
  276.       C := Retrieve;
  277.       if C<>RLEMARKER
  278.         then begin
  279.           R := C;
  280.           LastChar := C
  281.           end
  282.         else begin
  283.             C := Retrieve;
  284.             if C=NUL
  285.               then begin
  286.                   R := RLEMARKER;
  287.                   LastChar := RLEMARKER;
  288.                 end
  289.               else begin
  290.                 R := LastChar;
  291.                 RLE := ord(C)-2
  292.                end
  293.           end;
  294.      end;
  295.   UpdateCRC(Ord(R));
  296.   fGetC := R;
  297.   inc(FilePtr);
  298. end;
  299.  
  300. procedure HQX.fGetBlock(var Block; Size: word);
  301. var Buffer: TBuff absolute Block;
  302.     i: word;
  303. begin
  304.   if Size<>0 (* Size cannot be more than SizeOf(TBuff) ! *)
  305.     then for i := 1 to Size do Buffer[i] := ord(fGetC);
  306. end;
  307.  
  308. function HQX.fGetW: Word;
  309. var i: word;
  310. begin
  311.   fGetBlock(i,2);
  312.   fGetW := Swap(i); (* Automatically convert endianess *)
  313. end;
  314.  
  315. function HQX.fGetL: LongInt;
  316. var i: LongInt;
  317. begin
  318.   fGetBlock(i,4);
  319.   fGetL := SwapLong(i)
  320. end;
  321.  
  322. procedure HQX.fSeek(Position: LongInt);
  323. var C: char;
  324. begin
  325.   if FilePtr<Position (* Otherwise error?! *)
  326.     then repeat
  327.        C := fGetC;
  328.        until FilePtr=Position;
  329. end;
  330.  
  331. procedure HQX.fSkip(Position: LongInt);
  332. begin
  333.   if Position>0
  334.     then fSeek(FilePtr+Position)
  335. end;
  336.  
  337. (* Bug: Routine seems to get caught in an infinite loop ... *)
  338. {
  339. procedure HQX.fRewind(Position: LongInt);
  340. begin
  341.   if (RLE=0) and (Position<(SizeOf(TBuff)-8)) (* arbitrary *)
  342.     then repeat
  343.         dec(Cur);
  344.         if Cur=0
  345.           then Cur := SizeOf(TBuff);
  346.         dec(Position);
  347.       until Position=0;
  348. end;
  349. }
  350. constructor HQX.Init(var f: file; Orig: LongInt);
  351. var Temp : Word;
  352. begin
  353.   RLE := 0;
  354.   LastChar := NUL;
  355.   Loc := $80;
  356.   Ptr := 1;
  357.   Cur := 1;
  358.   GetMem(Bit_Buffer,SizeOf(TBuff)); { Doesn't check MemAvail! }
  359.   GetMem(DiskBuffer,SizeOf(TBuff));
  360.   DBuffSz  := 0;
  361.   DBuffPtr := 1;
  362.   FillChar(Bit_Buffer^,SizeOf(TBuff),NUL);
  363.   FilePtr := 0;
  364.   CRC     := $0000;
  365.   fif := @f;
  366.   Seek(fif^,Orig);
  367.   (* Assumes Orig points to position in file relative to the *)
  368.   (* "(This file ..." header in most BinHex files            *)
  369.   repeat until (ReadChar=':');
  370.   (* Read header information ...                             *)
  371.   FillChar(Header,SizeOf(Header),NUL);
  372.   Header.FName[0] := fGetC;
  373.   fGetBlock(Header.FName[1],Length(Header.FName));
  374.   Header.Version  := Ord(fGetC);
  375.   fGetBlock(Header.FType,4);
  376.   fGetBlock(Header.Author,4);
  377.   Header.Flags := fGetW;
  378.   Header.DataLen := fGetL;
  379.   Header.RsrcLen := fGetL;
  380.   Header.FileCRC := fCRC;
  381.   Header.CRC := fGetW; (* What is the CRC algorithm? ... *)
  382.  
  383. end;
  384.  
  385. destructor HQX.Done;
  386. begin
  387.   FreeMem(Bit_Buffer,SizeOf(TBuff));
  388.   FreeMem(DiskBuffer,SizeOf(TBuff));
  389. end;
  390.  
  391. end.
  392.